package com.atguigu.tingshu.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.account.AccountFeignClient;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.client.OrderFeignClient;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.pattern.TradeStrategy;
import com.atguigu.tingshu.order.pattern.factory.TradeStrategyFactory;
import com.atguigu.tingshu.order.service.AccountPayBizService;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.account.AccountDeductVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private AccountFeignClient accountFeignClient;

    @Autowired
    private OrderFeignClient orderFeignClient;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private TradeStrategyFactory tradeStrategyFactory;

    @Autowired
    private AccountPayBizService accountPayBizService;




    @Override
    public OrderInfoVo getOrderInfo(TradeVo tradeVo, Long userId) {

        TradeStrategy tradeStrategy = tradeStrategyFactory.getTradeStrategy(tradeVo.getItemType());

        return tradeStrategy.trade(tradeVo,userId);

/*
        String itemType = tradeVo.getItemType();

        Long itemId = tradeVo.getItemId();

        Integer trackCount = tradeVo.getTrackCount();

        //先初始化原始数据
        OrderInfoVo orderInfoVo = new OrderInfoVo();

        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();

        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();

        //初始化原始订单金额
        BigDecimal originalAmount = new BigDecimal("0.00");

        //初始化折扣订单金额
        BigDecimal derateAmount = new BigDecimal("0.00");

        //初始化订单订单金额
        BigDecimal orderAmount = new BigDecimal("0.00");


        //根据前端传来的tradeVo来判断是哪种支付类型
        //1.6 获取购买项目类型 付款项目类型: 1001-专辑 1002-声音 1003-vip会员

        //2. 处理购买项目类型：VIP套餐
        if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
            //2.1 远程调用用户服务得到VIP套餐信息
            VipServiceConfig vipServiceConfig = userFeignClient.getVipServiceConfig(tradeVo.getItemId()).getData();
            Assert.notNull(vipServiceConfig, "VIP套餐{}不存在", tradeVo.getItemId());
            //2.2 计算原金额、订单金额、减免金额
            originalAmount = vipServiceConfig.getPrice();
            orderAmount = vipServiceConfig.getDiscountPrice();
            //2.3 封装商品信息列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName("VIP套餐：" + vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            //2.4 如果存在优惠，封装优惠列表
            if (originalAmount.compareTo(orderAmount) == 1) {
                derateAmount = originalAmount.subtract(orderAmount);
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("VIP套餐限时优惠:" + derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }

        } else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
            //3. 处理购买项目类型：专辑
            //3.1 远程调用用户服务，判断是否重复购买专辑，如果已购过，则业务终止
            Long albumId = tradeVo.getItemId();
            Boolean flag = userFeignClient.isPaidAlbum(albumId).getData();
            Assert.isFalse(flag, "用户已购买专辑{}", albumId);
            //3.2 远程调用专辑服务，获取专辑价格以及折扣（普通用户折扣，VIP会员折扣）
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumInfo, "专辑{}不存在", albumId);
            BigDecimal price = albumInfo.getPrice();
            BigDecimal discount = albumInfo.getDiscount();
            BigDecimal vipDiscount = albumInfo.getVipDiscount();

            //3.3 远程调用用户服务，获取用户身份，是否为VIP
            Boolean isVIP = false;
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
            Assert.notNull(userInfoVo, "用户{}不存在", userId);
            if (userInfoVo.getIsVip().intValue() == 1
                    && userInfoVo.getVipExpireTime().after(new Date())) {
                isVIP = true;
            }

            //3.4 计算相关价格
            //3.4.1 暂时将订单价=原价
            originalAmount = price;
            orderAmount = originalAmount;
            //3.4.2 如果是普通用户，且存在普通用户折扣，则订单价=原价 100 *折扣 6
            if(!isVIP && discount.doubleValue() != -1){
                orderAmount = originalAmount.multiply(discount)
                        .divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
            }
            //3.4.3 如果是VIP会员，且存在VIP用户折扣，则订单价=原价*折扣
            if(isVIP && vipDiscount.doubleValue() != -1){
                orderAmount = originalAmount.multiply(vipDiscount)
                        .divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
            }

            //3.5 封装商品信息列表
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(albumId);
            orderDetailVo.setItemName("专辑："+albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(originalAmount);
            orderDetailVoList.add(orderDetailVo);

            //3.6 封装商品优惠列表
            if(originalAmount.compareTo(orderAmount) == 1){
                derateAmount = originalAmount.subtract(orderAmount);
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setRemarks("专辑折扣减免："+derateAmount);
                orderDerateVoList.add(orderDerateVo);
            }

        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
            //4.处理购买项目类型：声音
            //4.1 远程调用专辑服务，获取未购买声音列表
            Long trackId = tradeVo.getItemId();
            List<TrackInfo> trackInfoList = albumFeignClient.findWaitBuyTrackList(trackId, tradeVo.getTrackCount()).getData();
            Assert.notNull(trackInfoList, "不存在待结算声音", trackId);

            //4.2 远程调用专辑服务获取，专辑价格（声音单价）声音不支持折扣
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId()).getData();
            Assert.notNull(albumInfo, "专辑{}不存在", albumInfo.getId());
            BigDecimal price = albumInfo.getPrice();

            //4.3 计算相关价格
            originalAmount = price.multiply(new BigDecimal(trackInfoList.size()));
            orderAmount = originalAmount;

            //4.4 封装订单明细列表
            orderDetailVoList = trackInfoList.stream()
                    .map(trackInfo -> {
                        OrderDetailVo orderDetailVo = new OrderDetailVo();
                        orderDetailVo.setItemId(trackInfo.getId());
                        orderDetailVo.setItemName("声音："+trackInfo.getTrackTitle());
                        orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                        orderDetailVo.setItemPrice(price);
                        return orderDetailVo;
                    }).collect(Collectors.toList());

        }

        //5.封装订单VO对象
        //5.1 封装相关价格信息
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        //5.2 封装商品相关集合
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);

        //封装类型
        orderInfoVo.setItemType(itemType);

        //封装流水号,流水号需要防止用户重复提交
        //构建当前用户当前订单流水号
        //采用uuid生成本次流水号的值
        //并设置过期时间,注意,不能使用setnx,因为这样出问题就需要等待key到期才能释放锁

        String tradeNoKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;

        String tradeNo = UUID.randomUUID().toString().replaceAll("-","");

        //将流水号存到redis中
        redisTemplate.opsForValue().set(tradeNoKey,tradeNo,5, TimeUnit.MINUTES);

        //封装流水号
        orderInfoVo.setTradeNo(tradeNo);

        //封装时间戳
        orderInfoVo.setTimestamp(System.currentTimeMillis());
        //签名机制
        //调用生成签名的工具方法,底层就是MD5,对订单VO中所有属性值进行排序,拼接成字符串加密钥,进行MD5加密,返回签名值
        //将订单VO转为map对象,由于无法确定用户选择用什么支付方式,所以需要排出payway

        Map<String, Object> map = BeanUtil.beanToMap(orderInfoVo, false, true);
        //调用生成签名工具方法，底层就是Md5,对订单VO中所有属性值进行排序，拼接成字符串+秘钥，进行MD5加密，返回签名值
        String sign = SignHelper.getSign(map);

        orderInfoVo.setSign(sign);

        return orderInfoVo;*/
    }


    /**
     * 提交订单
     *
     * @param orderInfoVo
     * @return
     */
    @Override
    public Map<String, String> submitOrder(Long userId, OrderInfoVo orderInfoVo) {

        //业务校验,防止用户重复提交
        String tradeKey = RedisConstant.ORDER_TRADE_NO_PREFIX + userId;
        //KEYS[1]=流水号Key ARGV[1]=用户提交流水号值
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(script, Boolean.class);
        Boolean flag = (Boolean) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), orderInfoVo.getTradeNo());

        if (!flag){
            //走到这说明验证流水号失败
            throw new GuiguException(500,"校验流水号失败,请返回重新下单");
        }

        //2.验证签名，防止订单数据被篡改
        Map<String, Object> map = BeanUtil.beanToMap(orderInfoVo);
        map.remove("payWay");


        SignHelper.checkSign(map);

        //保存订单,以及订单明细,以及订单优惠
        OrderInfo orderInfo = this.saveOrderInfo(userId,orderInfoVo);

        //支付方式：1101-微信 1102-支付宝 1103-账户余额
        String payWay = orderInfoVo.getPayWay();

        //4.处理付款方式为余额

        if (SystemConstant.ORDER_PAY_ACCOUNT.equals(payWay)){
            accountPayBizService.handlerAccountPay(orderInfo);
/*            //走到这说明付款方式是余额支付
            //远程调用账户服务扣减余额
            //封装扣减账户所需要的参数

            AccountDeductVo accountDeductVo = new AccountDeductVo();

            accountDeductVo.setUserId(userId);
            accountDeductVo.setOrderNo(orderInfo.getOrderNo());
            accountDeductVo.setAmount(orderInfo.getOrderAmount());
            accountDeductVo.setContent(orderInfo.getOrderTitle());

            Result result = accountFeignClient.checkAndDeduct(accountDeductVo);

            //判断远程调用的结果是否为200
            if (result.getCode() != 200){
                throw new GuiguException(result.getCode(),result.getMessage());
            }

            //扣减余额成功,则修改订单状态,该称已支付
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
            orderInfoMapper.updateById(orderInfo);

            //远程调用用户微服务完成虚拟物品发货

            //构建虚拟物品发货的对象
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
            userPaidRecordVo.setUserId(userId);
            userPaidRecordVo.setItemType(orderInfo.getItemType());
            //根据订单编号查询订单明细获取购买项目ID列表
            List<OrderDetail> orderDetails = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>()
                    .eq(OrderDetail::getOrderId, orderInfo.getId())
                    .select(OrderDetail::getItemId)
            );

            List<Long> itemIdList = orderDetails.stream().map(list -> list.getItemId()).collect(Collectors.toList());

            userPaidRecordVo.setItemIdList(itemIdList);

            Result result1 = userFeignClient.savePaidRecord(userPaidRecordVo);

            if (result1.getCode() != 200){
                throw new GuiguException(result1.getCode(),result1.getMessage());
            }*/
        }


        //x.封装响应结果 如果是微信支付，根据响应订单编号对接微信支付；如果是余额支付成功，返回订单编号，查询订单详情
        Map<String, String> mapResult = new HashMap<>();
        map.put("orderNo", orderInfo.getOrderNo());
        return mapResult;
    }


    /**
     * 保存订单详情(外加订单明细,订单优惠)
     * @param userId
     * @param orderInfoVo
     * @return
     */
    @Override
    public OrderInfo saveOrderInfo(Long userId, OrderInfoVo orderInfoVo) {

        //将VO对象转为PO对象
        OrderInfo orderInfo = BeanUtil.copyProperties(orderInfoVo, OrderInfo.class);

        orderInfo.setUserId(userId);

        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();

        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();

        if (CollUtil.isNotEmpty(orderDetailVoList)){
            OrderDetailVo orderDetailVo = orderDetailVoList.get(0);
            orderInfo.setOrderTitle(orderDetailVo.getItemName());
        }

        //1.3 设置订单编号 确保全局唯一且趋势递增 生成策略=当日+雪花算法
        String orderNo = DateUtil.today().replaceAll("-","") + IdUtil.getSnowflakeNextId();
        orderInfo.setOrderNo(orderNo);

        //设置订单支付状态
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);

        //保存订单信息

        orderInfoMapper.insert(orderInfo);

        Long orderId = orderInfo.getId();


        //获取到用户id了保存其他两个表

        //先保存order_detail

        if (CollUtil.isNotEmpty(orderDetailVoList)){
            //保存细节表

            List<OrderDetail> orderDetailList = orderDetailVoList.stream()
                    .map(VO -> {
                        OrderDetail orderDetail = BeanUtil.copyProperties(VO, OrderDetail.class);
                        orderDetail.setOrderId(orderId);
                        return orderDetail;
                    }).collect(Collectors.toList());

            orderDetailList.stream()
                    .forEach(o -> {
                        orderDetailMapper.insert(o);
                    });
        }

        //3.封装订单优惠对象,保存订单优惠明细
        if (CollUtil.isNotEmpty(orderDerateVoList)) {
            orderDerateVoList.stream().forEach(orderDerateVo -> {
                OrderDerate orderDerate = BeanUtil.copyProperties(orderDerateVo, OrderDerate.class);
                orderDerate.setOrderId(orderId);
                orderDerateMapper.insert(orderDerate);
            });
        }
        return orderInfo;

    }


}
